#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "process.h"
#include <memory>
#include "flv.h"
#include "Buffer.h"

#ifdef DEBUG
#include <errno.h>
int errno;
#endif

#define READ_ERROR(ret) if((ret) < 0) return (ret);
#define WRITE_UB(data, val) *(data) = ((val) & 0xff);

int _buf_mem(CBuffer *buf)
{
	buf->m_buffer.size = INIT_BUFFER_SIZE;
	buf->m_buffer.pos = 0;
	buf->m_buffer.fd = -1;
	buf->m_buffer.data =new uc[buf->m_buffer.size];
	if(buf->m_buffer.data == NULL)
		return -E_ERR;
	buf->m_buffer.type = BUF_MEM;
	return 0;
}
int _buf_file(CBuffer *buf, const char *file)
{
	errno_t i=_sopen_s(&buf->m_buffer.fd,file,_A_RDONLY,0,0);
	long length=_filelength(buf->m_buffer.fd);
	if(length < 0) {
		perror("Can not stat file");
		return -E_ERR;
	}
	buf->m_buffer.size =length;
	if(buf->m_buffer.fd == -1)
	{
		perror("buffer_open: can not open file");
		return -E_ERR;
	}
	buf->m_buffer.pos = 0;
	buf->m_buffer.type = BUF_FILE;
	buf->m_buffer.data=new uc[length];
	if(_read(buf->m_buffer.fd,buf->m_buffer.data,length) ==-1) {
		perror("buffer_open: can not mmap file");
		delete []buf->m_buffer.data;
		return -E_ERR;
	}
	return 0;
}
int _buf_stream(CBuffer * buf, const char *file)
{
	if(_buf_mem(buf) < 0) return -E_ERR;
	_sopen_s(&buf->m_buffer.fd,file, O_WRONLY | O_CREAT | O_TRUNC, S_IREAD|S_IWRITE|S_IEXEC,P_WAIT);
	if(buf->m_buffer.fd == -1)
	{
		perror("buffer_open: can not open file");
		free(buf->m_buffer.data);
		return -E_ERR;
	}
	buf->m_buffer.type = BUF_STREAM;
	return 0;
}
int __checkSize(CBuffer* buf, int size)
{
	if(buf->m_buffer.type == BUF_FILE) return 0;
	if(buf->m_buffer.pos + size < buf->m_buffer.size) return buf->m_buffer.size;
	while(buf->m_buffer.size <= buf->m_buffer.pos + size)
	{
		buf->m_buffer.size *= 2;
	}
	buf->m_buffer.data = (uc *)realloc(buf->m_buffer.data, buf->m_buffer.size);
	if(buf->m_buffer.data == NULL) return 0;
	return buf->m_buffer.size;
}

CBuffer* CBuffer::Create(const char *file, int type)
{
	CBuffer *buf;
	if(type != BUF_MEM && file == 0) return 0;
	buf = new CBuffer;
	if(buf == 0) return 0;
	switch(type)
	{
	case BUF_MEM:
		if(_buf_mem(buf) == 0)
			return buf;
		break;
	case BUF_FILE:
		if(_buf_file(buf, file) == 0)
		{
			return buf;
		}
		break;
	case BUF_STREAM:
		if(_buf_stream(buf, file) == 0)
			return buf;
		break;
	}
	free(buf);
	return 0;
}
void CBuffer::Free()
{
	switch(m_buffer.type)
	{
	case BUF_FILE:
		if(m_buffer.data) delete [] m_buffer.data;
		_close(m_buffer.fd);
		break;
	case BUF_MEM:
		free(m_buffer.data);
		break;
	case BUF_STREAM:
		Flush();
		_close(m_buffer.fd);
		free(m_buffer.data);
		break;
	}
	free(this);
}

int CBuffer::Seek(int off, int mode)
{
	switch (mode)
	{
	case BUF_SET:
		if(off >= (int)m_buffer.size || off < 0)
			return -E_SEEK;
		m_buffer.pos = off;
		return 0;
	case BUF_CUR:
		if(m_buffer.pos + off >= m_buffer.size || m_buffer.pos + off < 0)
			return -E_SEEK;
		m_buffer.pos += off;
		return 0;
	default:
		return -E_ERR;
	}
}
int CBuffer::Tell() 
{
	return m_buffer.pos;
}

const uc* CBuffer::GetData(int size)
{
	if(m_buffer.pos + size >= m_buffer.size) return 0;
	return (const uc *)m_buffer.data + m_buffer.pos;
}

int CBuffer::Copy(CBuffer *src, int src_offset, int size)
{
	if(src == NULL || size <= 0) return 0;	
	if(src_offset + size >= (int)src->m_buffer.size) return 0;
	if(__checkSize(this, size) <= 0) return 0;
	memcpy(m_buffer.data + m_buffer.pos, src->m_buffer.data + src_offset, size);
	m_buffer.pos += size;
	return size;
}

int CBuffer::WriteToFile(const char *file)
{
	int ret;
	if(m_buffer.type != BUF_MEM) return -1;
	_sopen_s(&m_buffer.fd,file, O_WRONLY | O_CREAT | O_TRUNC, S_IREAD|S_IWRITE|S_IEXEC,P_WAIT);
	if(m_buffer.fd == -1)
	{
		perror("buffer_open: can not open file");
		return -1;
	}
	ret = _write(m_buffer.fd, m_buffer.data, m_buffer.pos);
	if(ret != m_buffer.pos)
	{
		perror("buffer_writeToFile: Write failed");
		return -1;
	}
	return ret;
}

int CBuffer::Flush()
{
	int ret;
	if(m_buffer.type != BUF_STREAM) return -1;
	ret = _write(m_buffer.fd, m_buffer.data, m_buffer.pos);
	if(ret != m_buffer.pos)
	{
		perror("buffer_writeToFile: Write failed");
		return -1;
	}
	m_buffer.pos = 0;
	return ret;
}
int CBuffer::Write(const uc *data, int size)
{
	if(data == NULL) return 0;
	if(__checkSize(this, size) <= 0) return 0;
	memcpy(m_buffer.data + m_buffer.pos, data, size);
	m_buffer.pos += size;
	return size;
}
int CBuffer::Writec(ui val)
{
	if(__checkSize(this, 1) <= 0) return 0;
	WRITE_UB(m_buffer.data + m_buffer.pos++, val);
	return 1;
}
int CBuffer::WriteUI16(ui val) 
{
	int l = 0;
	l += Writec(val >> 8);
	l += Writec(val);
	return l;
}
int CBuffer::WriteUI24(ui val) 
{
	int l = 0;
	l += Writec(val >> 16);
	l += WriteUI16(val);
	return l;
}
int CBuffer::WriteUI32(ui val) 
{
	int l = 0;
	l += Writec(val >> 24);
	l += WriteUI24(val);
	return l;
}

int CBuffer::Readc(ui *c) 
{
	if(m_buffer.pos < m_buffer.size) {
		*c = 0xff & *(m_buffer.data + m_buffer.pos);
		m_buffer.pos++;
		return 0;
	}
	return -E_EOF;
}
int CBuffer::ReadUI16(ui *ui16)
{
	ui tmp;
	int ret;
	ret = Readc(&tmp);
	READ_ERROR(ret);
	*ui16 = (tmp<<8);
	ret = Readc(&tmp);
	READ_ERROR(ret);
	*ui16 |= tmp;
	return 0;
}
int CBuffer::ReadUI24(ui *ui24) 
{
	ui tmp;
	int ret;
	ret = ReadUI16(&tmp);
	READ_ERROR(ret);
	*ui24 = (tmp<<8);
	ret = Readc(&tmp);
	READ_ERROR(ret);
	*ui24 |= tmp;
	return 0;
}
int CBuffer::ReadUI32(ui *ui32) 
{
	ui tmp;
	int ret; 
	ret = ReadUI24(&tmp);
	READ_ERROR(ret);
	*ui32 = (tmp<<8);
	ret = Readc(&tmp);
	READ_ERROR(ret);
	*ui32 |= tmp;	
	return 0;
}